(* Pack consecutive duplicates of list elements into sublists. *)

(* 函数结构：
主函数pack接收一个列表参数list
内部定义辅助递归函数aux，携带current(当前分组)和acc(累积结果)两个累加器
模式匹配处理：
空列表情况直接返回空（仅当原始列表为空时触发）
单元素列表时将当前元素加入临时分组并存入结果
多元素列表时比较相邻元素，相同则继续累积，不同则创建新分组 *)

(* ((a :: current) :: acc) 和 (a :: current :: acc) 区别
::运算符的右结合性
a :: current :: acc会被解析为a :: (current :: acc)，这表示：
先将current和acc连接,再将a连接到前面,

而我们需要的是(a :: current) :: acc，这表示：
先将a加入current列表头部,再将这个新列表作为整体加入acc头部,

在分组算法中，current是当前分组的临时列表，acc是已完成分组的集合。当遇到新元素a时：
正确做法是将a先加入当前分组current，再将整个分组加入结果集acc
原写法会导致错误的嵌套结构，破坏分组逻辑
示例：处理[1;1;2]时：
正确版本会生成[[1;1]; [2]],错误版本会产生类型错误或错误嵌套的结构 *)

let pack list = 
  let rec aux current acc = function
    | [] -> [] (* Can only be reached if original list is empty *)
    | [x] -> (x :: current) :: acc
    | a :: (b :: _ as t) -> if a = b then aux (a :: current) acc t else aux [] ((a :: current) :: acc) t 
  in
  List.rev (aux [] [] list) 
;;

assert (pack [`a;`a;`a;`a;`b;`c;`c;`a;`a;`d;`d;`e;`e;`e;`e] = 
    [[`a;`a;`a;`a];[`b];[`c;`c];[`a;`a];[`d;`d];[`e;`e;`e;`e]]) ;;

